package com.lb.uts.util;

import com.lb.uts.constants.UTSConstants;
import com.lb.uts.entity.ScheduleJob;
import com.lb.uts.entity.CacheClient;
import com.lb.uts.service.IActiveClientService;
import com.lb.uts.service.manager.SchedulerManager;
import com.lb.uts.service.policy.ISchedulingPolicy;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 执行策略的工具
 */
public final class PolicyUtil {
    private static final Logger logger = LoggerFactory.getLogger(PolicyUtil.class);
    /**
     * 存储job对应的ip信息
     * key:jobName_JobGroup
     * value:执行器ip的List
     */
    public static Map<String, List<CacheClient>> JOB_IP_LIST = new ConcurrentHashMap<>();

    /**
     * 存储所有的任务信息
     */
    public static List<ScheduleJob> SCHEDULE_JOB_LIST;

    /**
     * 当执行器在刷新的时候，设为true
     * 当其他线程获取执行器时，会判断他的值，如果是true，则采用自旋的方式获取
     */
    public static volatile boolean JOB_IP_LIST_CHANGING = false;
    /**
     * 当任务在刷新的时候，设为true
     * 和执行器一样，也是一个判断标识
     */
    public static volatile boolean JOB_LIST_CHANGING = false;

    public static Map<String, ISchedulingPolicy> schedulingPolicyMap = new HashMap<>();

    /**
     * 替换新的任务
     * <p>
     * 当有任务变更时，需要替换变更的任务信息。
     * </p>
     */
    public static void replaceJob(ScheduleJob scheduleJob) {
        boolean isLoop = true;
        while (isLoop) {
            if (!JOB_LIST_CHANGING) {
                for (Iterator<ScheduleJob> itr = SCHEDULE_JOB_LIST.iterator(); itr.hasNext(); ) {
                    ScheduleJob job = itr.next();
                    if (job.equals(scheduleJob)) {
                        BeanUtils.copyProperties(scheduleJob, job);
                        isLoop = false;
                        break;
                    }
                }
            }
        }
    }

    /**
     * 获取缓存中的任务信息
     */
    public static ScheduleJob getById(String id) {
        while (true) { //采用自旋方式，数据量不大的情况，当数据量比较大时，考虑加入自旋次数限制
            if (!JOB_LIST_CHANGING) {
                for (Iterator<ScheduleJob> itr = SCHEDULE_JOB_LIST.iterator(); itr.hasNext(); ) {
                    ScheduleJob job = itr.next();
                    if (job.getJobId().equals(id)) {
                        return job;
                    }
                }
            }
        }
    }

    /**
     * 得到一个有用的执行器ip
     */
    public static String getIpBySystemName(String systemName) {
        while (true) {
            if (!JOB_IP_LIST_CHANGING) {
                List<CacheClient> jobIps = JOB_IP_LIST.get(systemName);
                if (CollectionUtils.isEmpty(jobIps)) {
                    //没有合适的执行器
                    return null;
                }
                //随机取出一个ip
                int count = jobIps.size();
                Random random = new Random();
                int next = random.nextInt(count);
                return jobIps.get(next).getIp();
            } else {
                //此时后台线程正在刷新内存，等待，继续尝试
                logger.info("后台线程正在更新ip list，等待");
            }
        }
    }


    public static void updateActiveInfo(String systemName, String host) {
        //修改次数
        List<CacheClient> cacheClients = JOB_IP_LIST.get(systemName);
        if (cacheClients != null) {
            for (CacheClient cacheClient : cacheClients) {
                if (cacheClient.getIp().equals(host)) {
                    cacheClient.setFailCount(cacheClient.getFailCount() + 1);
                    if (cacheClient.getFailCount() >= 3) {
                        //认为client端失败，移除，并且发送警告邮件
                        SpringContextUtil.getBean(IActiveClientService.class).deleteBySystemNameAndIp(systemName, host);
                    }
                    break;
                }
            }
        }
    }


    /**
     * @param systemName
     * @param host
     */
    public static void updateSuccessActiveInfo(String systemName, String host) {
        //修改次数
        List<CacheClient> cacheClients = JOB_IP_LIST.get(systemName);
        for (CacheClient cacheClient : cacheClients) {
            if (cacheClient.getIp().equals(host)) {
                cacheClient.setFailCount(0);
                break;
            }
        }
    }

    /**
     * 更新定时任务
     */
    public static void updateJobIpList(List<Map<String, String>> ipsStringMap) {
        JOB_IP_LIST_CHANGING = true;
        JOB_IP_LIST.clear();
        getIpList(ipsStringMap);
        JOB_IP_LIST_CHANGING = false;
    }

    /**
     * 根据系统名，缓存所有的执行器
     * key：systemName
     * value：List<CacheClient>
     */
    private static void getIpList(List<Map<String, String>> ipsStringMap) {
        if (ipsStringMap != null) {

            for (Map<String, String> map : ipsStringMap) {
                List<CacheClient> cacheClients = new ArrayList<>();
                String sysName = map.get("systemName");
                String[] ips = map.get("ips").split(",");
                for (String ip : ips) {
                    CacheClient cacheClient = new CacheClient();
                    cacheClient.setSystemName(sysName);
                    cacheClient.setIp(ip);
                    cacheClient.setFailCount(0);
                    cacheClient.setTotalFailCount(0L);
                    cacheClients.add(cacheClient);
                }
                JOB_IP_LIST.put(sysName, cacheClients);
            }
        }
    }

    /*
   刷新任务列表
    */
    public static void updateJobList(List<ScheduleJob> scheduleJobs) throws SchedulerException {
        JOB_LIST_CHANGING = true; //更改变更标识
        for (Iterator itr = SCHEDULE_JOB_LIST.iterator(); itr.hasNext(); ) {
            ScheduleJob inCacheJob = (ScheduleJob) itr.next();
            //双层循环
            boolean isLive = false;
            for (ScheduleJob scheduleJob : scheduleJobs) {
                if (scheduleJob.equals(inCacheJob)) {//包含定时任务，此时比较version
                    if (inCacheJob.getVersion() < scheduleJob.getVersion()) {
                        //需要更新
                        BeanUtils.copyProperties(scheduleJob, inCacheJob);
                        if (UTSConstants.JOB_STATUS_NORMAL.equals(inCacheJob.getJobStatus())) {
                            SpringContextUtil.getBean(SchedulerManager.class).resumeJob(inCacheJob);
                        } else if (UTSConstants.JOB_STATUS_DELETE.equals(inCacheJob.getJobStatus())) {
                            SpringContextUtil.getBean(SchedulerManager.class).deleteJob(inCacheJob);
                            itr.remove();
                        } else {
                            SpringContextUtil.getBean(SchedulerManager.class).pauseJob(inCacheJob);
                        }
                    }
                    isLive = true;
                    continue;
                }
            }
            if (!isLive) {
                //定时任务不存在了
                SpringContextUtil.getBean(SchedulerManager.class).deleteJob(inCacheJob);
                itr.remove();
            }
        }
        //找出新增的
        int addSize = scheduleJobs.size() - SCHEDULE_JOB_LIST.size();
        if (addSize > 0) {
            for (int i = 0; i < addSize; i++) {
                SCHEDULE_JOB_LIST.add(scheduleJobs.get(i));
                SpringContextUtil.getBean(SchedulerManager.class).addJob(scheduleJobs.get(i));
            }
        }
        JOB_LIST_CHANGING = false;
    }

    /**
     * 缓存存活的执行器
     */
    public static void initJobIpList(List<Map<String, String>> ipsStringMap) {
        getIpList(ipsStringMap);
    }
}
